package miservice

import (
	"crypto/rc4"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os"
	"path"
	"strings"
)

func parseDesc(desc string) (string, string) {
	name := ""
	specialChars := "-—{「[【(（<《"
	for _, d := range desc {
		if strings.ContainsRune(specialChars, d) {
			return name, "  # " + desc[len(name)+1:]
		}
		if d == ' ' {
			name += "_"
		} else {
			name += string(d)
		}
	}
	return name, ""
}

func makeLine(siid, iid int, desc, comment string, format string, readable bool) string {
	var value string
	if format == "python" {
		value = fmt.Sprintf("(%d, %d)", siid, iid)
	} else {
		value = fmt.Sprintf("%d", iid)
	}

	prefix := ""
	if !readable {
		prefix = "_"
	}

	return fmt.Sprintf("    %s%s = %s%s\n", prefix, desc, value, comment)
}

func getSpec(kind string, specs map[string]string) map[string]string {
	if kind == "" {
		return specs
	}
	var ret = make(map[string]string)
	for k, v := range specs {
		if k == kind {
			return map[string]string{k: v}
		} else if strings.Contains(k, kind) {
			ret[k] = v
		}
	}
	return ret
}

func loadSpec(p string) (map[string]string, error) {
	f, err := os.Open(p)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	j := json.NewDecoder(f)
	var specs map[string]string
	err = j.Decode(&specs)
	if err != nil {
		return nil, err
	}
	return specs, nil
}

type MiotSpecInstances struct {
	Instances []struct {
		Status  string `json:"status"`
		Model   string `json:"model"`
		Version int    `json:"version"`
		Type    string `json:"type"`
		Ts      int    `json:"ts"`
	} `json:"instances"`
}

func (s *IOService) IotSpec(kind string) ( /*map[string]string,*/ []string, error) {
	if kind == "" || !strings.HasPrefix(kind, "urn") {
		p := path.Join(os.TempDir(), "miot-spec.json")
		specs, err := loadSpec(p)
		if err != nil {
			r, err := s.account.client.Get("http://miot-spec.org/miot-spec-v2/instances?status=all")
			if err != nil {
				return nil, err
			}
			defer r.Body.Close()
			j := json.NewDecoder(r.Body)
			var instanceSpec MiotSpecInstances
			err = j.Decode(&instanceSpec)
			if err != nil {
				return nil, err
			}
			specs = make(map[string]string)
			for _, v := range instanceSpec.Instances {
				specs[v.Model] = v.Type
			}
			f, err := os.Create(p)
			if err == nil {
				defer f.Close()
				json.NewEncoder(f).Encode(specs)
			}
			specs = getSpec(kind, specs)
		}
		if len(specs) != 1 {
			instances := make([]string, 0, len(specs))
			for _, v := range specs {
				instances = append(instances, v)
			}
			return instances, nil
		}
		for _, v := range specs {
			kind = v
			break
		}
	}
	u := "http://miot-spec.org/miot-spec-v2/instance?type=" + kind
	r, err := s.account.client.Get(u)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	type AutoGenerated struct {
		Instances []string `json:"instances"`
	}
	j := json.NewDecoder(r.Body)
	var instanceSpec AutoGenerated
	err = j.Decode(&instanceSpec)
	if err != nil {
		return nil, err
	}
	return instanceSpec.Instances, nil
}

func (s *IOService) IotDecode(ssecurity string, nonce string, data string, gzip bool) (interface{}, error) {
	signNonce, err := signNonce(ssecurity, nonce)
	if err != nil {
		return nil, err
	}
	key, err := base64.StdEncoding.DecodeString(signNonce)
	if err != nil {
		return nil, err
	}
	cipher, err := rc4.NewCipher(key)
	if err != nil {
		return nil, err
	}

	cipher.XORKeyStream(key[:1024], key[:1024])

	encryptedData, err := base64.StdEncoding.DecodeString(data)
	if err != nil {
		return nil, err
	}
	decrypted := make([]byte, len(encryptedData))
	cipher.XORKeyStream(decrypted, encryptedData)

	if gzip {
		decrypted, err = unzip(decrypted)
		if err != nil {
			return nil, err
		}
	}

	var result interface{}
	err = json.Unmarshal(decrypted, &result)
	if err != nil {
		return nil, err
	}
	return result, nil
}
